package lab.cadl.lirui.markdown.core.utils

import java.lang.StringBuilder

class Escaping {
    companion object {
        const val ESCAPABLE_CHARS = "[!\"#$%&\'()*+,./:;<=>?@\\[\\\\\\]^_`{|}~-]"
        val ESCAPABLE_RE = Regex(ESCAPABLE_CHARS)

        val ESCAPED_CHAR = "\\\\$ESCAPABLE_CHARS"

        val ENTITY = "&(?:#x[a-f0-9]{1,8}|#[0-9]{1,8}|[a-z][a-z0-9]{1,31});"
        val ENTITY_RE = Regex(ENTITY)

        val ENTITY_OR_ESCAPED_CHAR_RE = Regex("\\\\$ESCAPABLE_CHARS|$ENTITY", RegexOption.IGNORE_CASE);

        val BACKSLASH_OR_AMP_RE = Regex("[\\\\&]")
        val XML_SPECIAL = Regex("[&<>\"]")

        val ESCAPE_IN_URI_RE = Regex("(%[a-fA-F0-9]{0,2}|[^:/?#@!\$&'()*+,;=a-zA-Z0-9\\-._~])")

        val WHITESPACE_RE = Regex("[ \t\r\n]+")

        fun unescaping(s: String): String {
            return if (BACKSLASH_OR_AMP_RE.find(s) != null) {
                replaceAll(ENTITY_OR_ESCAPED_CHAR_RE, s) {
                    if (it[0] == '\\') {
                        it.substring(1, it.length)
                    } else {
                        Html5Entities.entityToString(it)
                    }
                }
            } else {
                s
            }
        }

        fun replaceAll(pattern: Regex, input: String, replacer: (s: String) -> String): String {
            val matcher = pattern.toPattern().matcher(input)

            val sb = StringBuilder(input.length + 16)
            var lastEnd = 0

            while (matcher.find()) {
                sb.append(input, lastEnd, matcher.start())
                sb.append(replacer(matcher.group()))
                lastEnd = matcher.end()
            }

            if (lastEnd != input.length) {
                sb.append(input, lastEnd, input.length)
            }

            return sb.toString()
        }

        fun normalizeReference(s: String): String {
            return WHITESPACE_RE.replace(s.trim()) {
                " "
            }
        }
    }

}